package com.info.zhiduoduo.pay.sdk.dayspaysdk.core.util;

import net.sf.json.JSONNull;
import net.sf.json.JSONObject;

import org.apache.log4j.Logger;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * ���乤�߿�
 * @author xie
 *
 */
public class Reflecter {

	private static Logger logger = Logger.getLogger(Reflecter.class.getName());

	//������_��ͷ�ģ���ʾ��ʱ����
	//������_�����ģ�ȥ������_

	//֧��int|long|boolean|byte|short|float|char|
	//Integer|Long|Boolean|Byte|Short|Float|String|Date|BigDecimal|BigInteger|List|



	/**
	 * �ж��Ƿ�������������ͣ������ڷ�����ƣ�������ֱ�ӱ�Ϊ�ַ�����������Ҫ��㷴��
	 * @param gt
	 * @return
	 */
	public static boolean isBaseType(Type gt) {

		return gt.equals(int.class) ||
				gt.equals(long.class) ||
				gt.equals(double.class) ||
				gt.equals(boolean.class) ||
				gt.equals(byte.class) ||
				gt.equals(short.class) ||
				gt.equals(float.class) ||
				gt.equals(char.class) ||
				gt.equals(Integer.class) ||
				gt.equals(Long.class) ||
				gt.equals(Double.class) ||
				gt.equals(Boolean.class) ||
				gt.equals(Byte.class) ||
				gt.equals(Short.class) ||
				gt.equals(Float.class) ||
				gt.equals(Character.class) ||
				gt.equals(String.class) ||
				gt.equals(BigDecimal.class) ||
				gt.equals(BigInteger.class);
	}

	private static boolean isIgnore(Type gt,Class<?>[] ignores) {

		if(ignores != null) {

			for(Class<?> ignore:ignores) {

				if(gt.equals(ignore))
					return true;
			}
		}

		return gt.equals(Logger.class);
	}

	/**
	 * �ж��Ƿ��������͵Ķ��󣬶����Զ���װsql�������������������͵Ķ�����sql��䲻��Ҫ�Ըö����ֵ������
	 * @param gt
	 * @return
	 */
	public static boolean isNumberType(Type gt) {

		return gt.equals(int.class) ||
				gt.equals(long.class) ||
				gt.equals(double.class) ||
				gt.equals(short.class) ||
				gt.equals(float.class) ||
				gt.equals(Integer.class) ||
				gt.equals(Long.class) ||
				gt.equals(Double.class) ||
				gt.equals(Short.class) ||
				gt.equals(Float.class) ||
				gt.equals(BigDecimal.class) ||
				gt.equals(BigInteger.class);
	}

	/**
	 * �ж��Ƿ����ڵ��������ͣ������ڷ�����ƣ����������ͨ��DateUtil��任Ϊһ����ʽ���ַ���
	 * @param gt
	 * @return
	 */
	public static boolean isDate(Type gt) {

		if(gt instanceof Class) {

			Class<?> c = (Class<?>)gt;

			return c.equals(Date.class) ||
					c.equals(java.sql.Date.class) ||
					c.equals(java.sql.Time.class) ||
					c.equals(java.sql.Timestamp.class) ||
					c.getName().equals("com.ibm.db2.jcc.DBTimestamp");//ibm jdk
		} else {

			return gt.equals(Date.class) ||
					gt.equals(java.sql.Date.class) ||
					gt.equals(java.sql.Time.class) ||
					gt.equals(java.sql.Timestamp.class) ||
					gt.equals("com.ibm.db2.jcc.DBTimestamp");//ibm jdk
		}
	}

	/**
	 * �ж��Ƿ񼯺���
	 * @param c
	 * @return
	 * @throws Exception
	 */
	public static boolean isCollection(Class<?> c) throws Exception {

		return Collection.class.isAssignableFrom(c);

	}

	/**
	 * �ж��Ƿ�map��
	 * @param c
	 * @return
	 */
	public static boolean isMap(Class<?> c) {

		return Map.class.isAssignableFrom(c);

	}

	/**
	 * �ַ�����һ�����󣬶��������͵Ķ���ͨ��DataUtil�����ת��
	 * @param o
	 * @return
	 */
	public static String toString(Object o) {

		if(o == null)
			return "";

		Type gt = o.getClass();
		if(isNumberType(gt))
			return o.toString();
		else if(isDate(gt))
			return DateUtil.yyyy_MM_dd_HH_mm_ss((Date)o);
		else
			return o.toString();
	}


	/**
	 * ��һ�����������װsql���ʱ��ֵӳ�䣬�����ڶ�db�������sql��ʱ�Ĳ���
	 * @param o
	 * @return
	 */
	
	/**
	 * ���ݸö��������Ӧdb�����ͣ����ö������ת��Ϊdb����
	 * @param typeName
	 * @param value
	 * @return
	 * @throws Exception
	 */
	public static Object toDbObject(String typeName,Object value) throws Exception {

		if("BIGINT".equalsIgnoreCase(typeName))
			return Long.parseLong(value.toString());
		else if("DECIMAL".equalsIgnoreCase(typeName))
			return new BigDecimal(value.toString());
		else if("INTEGER".equalsIgnoreCase(typeName))
			return Integer.parseInt(value.toString());
		else if("SMALLINT".equalsIgnoreCase(typeName))
			return Short.parseShort(value.toString());
		else if("TIMESTAMP".equalsIgnoreCase(typeName)) {
			if(Strings.isNullOrEmpty(value)) 
				return null;
			else if("NULL".equalsIgnoreCase(value.toString())) 
				return null;
			else if("CURRENT TIMESTAMP".equalsIgnoreCase(value.toString())) 
				return new Date();
			else 
				return DateUtil.yyyy_MM_dd_HH_mm_ss(value.toString());
		}

		return value;
	}
	/**
	 * ���ݸ��ַ���������Ӧdb�����ͣ������ַ�������ת��Ϊdb����
	 * @param typeName
	 * @param value
	 * @return
	 * @throws Exception
	 */
	public static Object toDbObject(String typeName,String value) throws Exception {

		return toDbObject(typeName,(Object)value);
	}

	/**
	 * ��java��db���໥�����У�����java����ʱ��������_��ͷ������java�﷨�Ĺؼ��֣���_��β������default��
	 * @param c
	 * @return
	 */
	public static ArrayList<String> getUsefulFiledName(Class<?> c) {

		ArrayList<String> ret = new ArrayList<String>();

		Field[] fs = c.getDeclaredFields();
		for(Field f:fs) {

			String fn = f.getName();
			if(fn.startsWith("_"))
				continue;
			if(fn.endsWith("_"))
				fn = fn.substring(0,fn.length()-1);

			ret.add(fn);
		}

		return ret;
	}

	//ͨ��setter��ȡ����obj
	/**
	 * ��dbֵ��java hashMap��ʽ��java bean�ķ����У�
	 * ��haspMap values��key��class c�ı�����Ӧ������
	 * ��values��value����c��ֵ��args��c���캯���Ĳ���
	 * @param c
	 * @param values
	 * @param args
	 * @return
	 * @throws Exception
	 */
	public static Object setter(Class<?> c,HashMap<String,Object> values,Object ...args) throws Exception {

		logger.debug("reflect a class:"+c.getName());

		Object o = makeInstance(c.getName(),args);

		Field[] fs = c.getDeclaredFields();
		for(Field f:fs) {
			String fn = f.getName();
			Object val = values.get(fn);
			if(val == null && fn.endsWith("_"))
				val = values.get(fn.substring(0,fn.length()-1));
			if(val == null)
				val = values.get(fn.toUpperCase());
			if(val == null && fn.endsWith("_"))
				val = values.get(fn.substring(0,fn.length()-1).toUpperCase());

			logger.debug("field name-value:"+fn+"-"+val);

			if(f != null && !Strings.isNullOrEmpty(val) && !f.toGenericString().contains(" final ")) {

				Type gt = f.getGenericType();

				boolean af = f.isAccessible();
				f.setAccessible(true);

				if(gt.equals(int.class) || gt.equals(Integer.class)) {
					f.set(o,Integer.parseInt(val.toString()));
				} else if(gt.equals(long.class) || gt.equals(Long.class)) {
					f.set(o,Long.parseLong(val.toString()));
				} else if(gt.equals(double.class) || gt.equals(Double.class)) {
					f.set(o,Double.parseDouble(val.toString()));
				} else if(gt.equals(boolean.class) || gt.equals(Boolean.class)) {
					f.set(o,Boolean.parseBoolean(val.toString()));
				} else if(gt.equals(byte.class) || gt.equals(Byte.class)) {
					f.set(o,Byte.parseByte(val.toString()));
				} else if(gt.equals(short.class) || gt.equals(Short.class)) {
					f.set(o,Short.parseShort(val.toString()));
				} else if(gt.equals(float.class) || gt.equals(Float.class)) {
					f.set(o,Float.parseFloat(val.toString()));
				} else if(gt.equals(char.class) || gt.equals(Character.class)) {
					f.set(o,val.toString().toCharArray()[0]);
				} else if(gt.equals(String.class)) {
					f.set(o,val.toString());
				} else if(gt.equals(BigDecimal.class)) {
					f.set(o,new BigDecimal(val.toString()));
				} else if(gt.equals(BigInteger.class)) {
					f.set(o,new BigInteger(val.toString()));
				} else if(isDate(gt)) {
					f.set(o,DateUtil.yyyy_MM_dd_HH_mm_ss(val.toString()));
				}

				f.setAccessible(af);
			}
		}

		return o;
	}

	//ͨ��setter��ȡ����obj
	/**
	 * ��dbֵ(����¼)��java arrayList(key��value)��ʽ��java bean�ķ����У�
	 * ��arrayList key��class c�ı�����Ӧ������
	 * ��arrayList��value����c��ֵ��args��c���캯���Ĳ���
	 * @param c
	 * @param cols
	 * @param vals
	 * @param args
	 * @return
	 * @throws Exception
	 */
	public static Object setter(Class<?> c,ArrayList<String> cols,ArrayList<Object> vals,Object ...args) throws Exception {

		if(cols.size() != vals.size()) {
			logger.warn("cols size invalid by vals size,cs:"+cols.size()+" by vs: "+vals.size());
			return null;
		}

		HashMap<String,Object> values = new HashMap<String,Object>();
		for(int i = 0;i < cols.size();i++) {
			values.put(cols.get(i),vals.get(i));
		}

		return setter(c,values);
	}

	
	//ͨ��setter��ȡobj����
	/**
	 * ��dbֵ(���¼)��java arrayList(key��value)��ʽ��java bean��hashMap�ķ����У�
	 * ��arrayList cols��class c�ı�����Ӧ������
	 * ��arrayList��vals����c��ֵ���γ�hashMap��args��c���캯���Ĳ���
	 * @param c
	 * @param keyName
	 * @param cols
	 * @param vals
	 * @param args
	 * @return
	 * @throws Exception
	 */
	public static HashMap<String,Object> setter(Class<?> c,String keyName, ArrayList<String> cols,ArrayList<Map<String, ArrayList<Object>>> vals,Object ...args) throws Exception {

		if(cols.size() != vals.get(0).size()) {
			logger.warn("cols size invalid by vals size,cs:"+cols.size()+" by vs(0): "+vals.get(0).size());
			return null;
		}

		HashMap<String,Object> ret = new HashMap<String,Object>();

		int keyIndex = 0;
		for(int i = 0;i < cols.size();i++) {
			if(keyName.equalsIgnoreCase(cols.get(i))) {
				keyIndex = i;
				break;
			}
		}

		for(int i = 0;i < vals.size();i++) {
			for(Map.Entry<String, ArrayList<Object>> entity: vals.get(i).entrySet()){
				ArrayList<Object> val = entity.getValue();
				String key = val.get(keyIndex).toString();

				HashMap<String,Object> values = new HashMap<String,Object>();
				for(int j = 0;j < cols.size();j++) {
					values.put(cols.get(j),val.get(j));
				}

				ret.put(key, setter(c,values));
			}
		}

		return ret;
	}

	/**
	 * ͨ���������������c��ʵ��
	 * @param c ��c
	 * @param args ��c���캯���ı���
	 * @return
	 * @throws Exception
	 */
	public static Object makeInstance(Class<?> c,Object...args) throws Exception {

		if(args == null || args.length == 0)
			return c.newInstance();

		Constructor<?>[] cts = c.getDeclaredConstructors();
		for(Constructor<?> ct:cts) {

			Class<?>[] pts = ct.getParameterTypes();
			if(args != null && args.length == pts.length) {

				logger.debug(c.getName()+" param length:"+args.length);
				return c.getConstructor(pts).newInstance(args);
			}
		}

		return c.newInstance();
	}

	/**
	 * ͨ���������������className��ʵ��
	 * @param className ��c
	 * @param args ��c���캯���ı���
	 * @return
	 * @throws Exception
	 */
	public static Object makeInstance(String className,Object...args) throws Exception {

		logger.debug("make instance:"+className);

		Class<?> c = Class.forName(className);

		return makeInstance(c,args);
	}

	/**
	 * ��json��ʽ���ַ���ת��ΪhashMap
	 * @param datas
	 * @return
	 */
	public static LinkedHashMap<String,Object> jsonToMap(String datas) {

		JSONObject obj = JSONObject.fromObject(datas);

		return jsonToMap(obj);
	}

	/**
	 * ��json����ת��ΪhashMap
	 * @param obj
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static LinkedHashMap<String,Object> jsonToMap(JSONObject obj) {

		LinkedHashMap<String,Object> ret = new LinkedHashMap<String,Object>();

		if(obj == null)
			return ret;

		Set<String> keys = obj.keySet();
		for(String key:keys) {
			Object value = obj.get(key);
			try {
				if(value instanceof JSONNull){
					value = null;
				}
				if(key.toLowerCase().endsWith("time") || key.toLowerCase().endsWith("date")){
					String[] formats = {"yyyy-MM-dd HH:mm:ss.SSS", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd", "yyyyMMdd","yyyyMMddHHmmss"};
					for(int i=0; i<formats.length; i++){
						try {
							if(formats[i].length() == Strings.trim(obj.get(key)).length()) {
								SimpleDateFormat sdf = new SimpleDateFormat(formats[i]);
								ret.put(key, sdf.parse(Strings.trim(obj.get(key))));
								break;
							}
						} catch(Exception e){
							logger.error(Strings.toString(e));
						}
					}
					if(null == ret.get(key)){
						ret.put(key, value);
					}
				} else {
					ret.put(key, value);
				}
			} catch(Exception e){
				logger.error(e);
				ret.put(key, value);
			}
		}

		return ret;
	}

	/**
	 * ��json����ת��ΪhashMap
	 * @param obj
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static LinkedHashMap<String,String> jsonToMapStr(String datas) {

		JSONObject obj = JSONObject.fromObject(datas);

		LinkedHashMap<String,String> ret = new LinkedHashMap<String,String>();

		if(obj == null)
			return ret;

		Set<String> keys = obj.keySet();
		for(String key:keys) {
			Object value = obj.get(key);
			if(value instanceof JSONNull){
				value = "";
			}
			ret.put(key, Strings.toString(value));
		}

		return ret;
	}

	/**
	 * �ϲ���������a��b��b����a�ĺ���
	 * @param a
	 * @param b
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T[] concat(T[] a, T[] b) {

		final int alen = a.length;
		final int blen = b.length;
		if (alen == 0) {
			return b;
		}  
		if (blen == 0) {
			return a;
		}
		final T[] result = (T[])Array.newInstance(a.getClass().getComponentType(), alen + blen);
		System.arraycopy(a, 0, result, 0, alen);
		System.arraycopy(b, 0, result, alen, blen);
		return result;

	}

	/**
	 * ��ȡmap��ֵ����
	 * @param map
	 * @return
	 */
	public static Collection<?> getMapValues(Map<?,?> map) {

		List<Object> list = new ArrayList<Object>();

		Iterator<?> iter = map.values().iterator();

		while(iter.hasNext()) {
			list.add(iter.next());
		}

		return list;
	}

	/**
	 * ��¡һ������
	 * @param src Դ����
	 * @param subClass ����
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static Collection<?> cloneCollection(Collection<?> src,Class<?> subClass) throws Exception {

		if(src == null)
			return null;

		Collection<Object> ret = (Collection<Object>)makeInstance(src.getClass());

		Iterator<?> iter = src.iterator();

		while(iter.hasNext()) {

			ret.add(clone(iter.next(),null,subClass));
		}

		return ret;
	}

	/**
	 * ��¡һ������
	 * @param src Դ����
	 * @return
	 * @throws Exception
	 */
	public static Collection<?> cloneCollection(Collection<?> src) throws Exception {
		if(src == null)
			return null;
		Class<?> subClass = src.iterator().next().getClass();
		return cloneCollection(src,subClass);
	}

	/**
	 * ��¡һ��map
	 * @param src Դmap
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static Map<?,?> cloneMap(Map<?,?> src) throws Exception {

		if(src == null)
			return null;

		Map<Object,Object> ret = (Map<Object,Object>)makeInstance(src.getClass());
		Set<?> keys = src.keySet();

		for(Object key:keys) {
			ret.put(clone(key), clone(src.get(key)));
		}

		return ret;
	}

	/**
	 * ��¡һ������
	 * ����¡����ı������������͡�ʱ�䡢���ϡ�map����������������ܱ���¡
	 * @param src
	 * @return
	 * @throws Exception
	 */
	public static Object clone(Object src) throws Exception {

		if(src == null)
			return null;

		return clone(src,null,src.getClass());
	}

	/**
	 * ��¡һ�����󣬲����Կ�¡�ö����һЩ������
	 * ����¡����ı������������͡�ʱ�䡢���ϡ�map����������������ܱ���¡
	 * @param src Դ����
	 * @param ignores �����Կ�¡��Դ����ı���������
	 * @return
	 * @throws Exception
	 */
	public static Object clone(Object src,Class<?>[] ignores) throws Exception {

		if(src == null)
			return null;

		return clone(src,ignores,src.getClass());
	}
	/**
	 * ��¡һ����������ݵ���һ������Ҫ����������ı������ƾ����غ����ԣ�
	 * ����¡����ı������������͡�ʱ�䡢���ϡ�map����������������ܱ���¡
	 * @param src Դ����
	 * @param ignores �����Կ�¡��Դ����ı���������
	 * @param destClass Ŀ��������
	 * @param destObjectArgs Ŀ������๹�캯���Ĳ���
	 * @return
	 * @throws Exception
	 */
	public static Object clone(Object src,Class<?>[] ignores,Class<?> destClass,Object...destObjectArgs) throws Exception {

		if(src == null)
			return null;

		if(isIgnore(src.getClass(),ignores))
			return src;

		if(isBaseType(src.getClass()) || isDate(src.getClass()))
			return src;

		logger.debug("clone a class:"+src.getClass()+"->"+destClass);

		Object dest = makeInstance(destClass,destObjectArgs);

		Field[] fsSrc = concat(src.getClass().getDeclaredFields(),src.getClass().getSuperclass().getDeclaredFields());
		Field[] fsTmp = concat(destClass.getDeclaredFields(),destClass.getSuperclass().getDeclaredFields());
		HashMap<String,Field> fsDes = new HashMap<String,Field>();
		for(Field f:fsTmp) {
			fsDes.put(f.getName(), f);
		}
		for(Field fSrc:fsSrc) {

			boolean afSrc = fSrc.isAccessible();
			fSrc.setAccessible(true);
			try {
				Field f = fsDes.get(fSrc.getName());

				Object val = fSrc.get(src);
				if(f == null)
					logger.debug("field is null :"+fSrc.getName());
				if(val == null)
					logger.debug("value is null :"+fSrc.getName());

				if(f != null && val != null && !f.toGenericString().contains(" final ")) {

					Type gt = f.getGenericType();

					boolean af = f.isAccessible();
					f.setAccessible(true);

					if(isBaseType(gt) || isDate(gt)) {
						f.set(dest,val);
					} else if(isCollection(val.getClass())) {

						f.set(dest,cloneCollection((Collection<?>)val));
					} else if(isMap(val.getClass())) {
						f.set(dest,cloneMap((Map<?,?>)val));
					} else {
						Object o = clone(val,ignores,val.getClass());
						f.set(dest,o);
					}

					f.setAccessible(af);
				}
			} catch(Exception e) {
				logger.error(Strings.toString(e));
			} finally {
				fSrc.setAccessible(afSrc);
			}
		}

		return dest;
	}
}
